/**
 * Copyright (C) 2015-2018 IQIYI
 * All rights reserved.
 *
 * Author     : 王海斌
 * E-mail     : wanghaibin@qiyi.com
 * Version    :
 * Date       : Wed 22 Jul 2020 11:19:44 PM CST
 * Destription:
 */


#include <signal.h>
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>
#include <sys/syscall.h>
#define gettid() syscall(__NR_gettid)

#define BUFFER_SIZE 1025
int MAX_LIST = 2;

// 测试aio_read , aio_error, aio_return 函数
int test_aio_read() {
    struct aiocb rd; //aio操作所需结构体
    int fd,ret,couter;

    fd = open("test.txt",O_RDONLY, 0644);
    if(fd < 0) {
        perror("test.txt");
    }
    //将rd结构体清空
    bzero(&rd,sizeof(rd));

    rd.aio_buf = malloc(BUFFER_SIZE + 1); //为rd.aio_buf分配空间
    //填充rd结构体
    rd.aio_fildes = fd;
    rd.aio_nbytes = BUFFER_SIZE;
    rd.aio_offset = 0;

    ret = aio_read(&rd); //进行异步读操作
    if(ret < 0) {
        perror("aio_read");
        exit(1);
    }
    //do other things

    couter = 0;
    //  循环等待异步读操作结束
    while(aio_error(&rd) == EINPROGRESS) { //一直不停的查询(消耗cpu)
         printf("第%d次\n",++couter);
    }

    //获取异步读返回值
    ret = aio_return(&rd);
    printf("\n\n返回值为:%d\n",ret);
    printf("%s\n",rd.aio_buf);
    free((char*)rd.aio_buf);
    close(fd);
    return 0;
}

// 测试aio_suspend 函数
int test_aio_suspend() {

    struct aiocb rd; //aio操作所需结构体
    int fd,ret,couter;

    //cblist链表
    struct aiocb *aiocb_list[2];

    fd = open("test.txt",O_RDONLY, 0644);
    if(fd < 0) {
        perror("test.txt");
    }

    //将rd结构体清空
    bzero(&rd,sizeof(rd));

    //为rd.aio_buf分配空间
    rd.aio_buf = malloc(BUFFER_SIZE + 1);

    //填充rd结构体
    rd.aio_fildes = fd;
    rd.aio_nbytes =  BUFFER_SIZE;
    rd.aio_offset = 0;

    //将读fd的事件注册
    aiocb_list[0] = &rd;

    //进行异步读操作
    ret = aio_read(&rd);
    if(ret < 0) {
        perror("aio_read");
        exit(1);
    }

    printf("我要开始等待异步读事件完成\n");
    ret = aio_suspend(aiocb_list,MAX_LIST,NULL); //阻塞等待异步读事件完成
    ret = aio_return(&rd);//获取异步读返回值
    free((char*)rd.aio_buf);
    printf("\n\n返回值为:%d\n",ret);
    return 0;
}

// 异步io采用同步等待测试方法
int test_lio_listio_wait () {
    struct aiocb *listio[2];
    struct aiocb rd,wr;
    int fd,ret;

    //异步读事件
    fd = open("test1.txt",O_RDONLY, 0644);
    if(fd < 0) {
        perror("test1.txt");
    }

    bzero(&rd,sizeof(rd));

    rd.aio_buf = (char *)malloc(BUFFER_SIZE);
    if(rd.aio_buf == NULL) {
        perror("aio_buf");
    }

    rd.aio_fildes = fd;
    rd.aio_nbytes = BUFFER_SIZE;
    rd.aio_offset = 0;
    rd.aio_lio_opcode = LIO_READ;   ///lio操作类型为异步读

    //将异步读事件添加到list中
    listio[0] = &rd;

    //异步写事件,
    fd = open("test2.txt",O_WRONLY | O_APPEND);
    if(fd < 0) {
        perror("test2.txt");
    }

    bzero(&wr,sizeof(wr));

    const char * file2info = "test_whb2_data\r\naaa,bbb";
    wr.aio_buf = (char *)malloc(BUFFER_SIZE);
    if(wr.aio_buf == NULL) {
        perror("aio_buf");
    }
    memcpy((void*)wr.aio_buf, file2info, strlen(file2info));

    wr.aio_fildes = fd;
    rd.aio_offset = 0; //因为有O_APPEND 所以offset即使设置了也被忽略
    wr.aio_nbytes = strlen(file2info);

    wr.aio_lio_opcode = LIO_WRITE;   ///lio操作类型为异步写

    //将异步写事件添加到list中
    listio[1] = &wr;

    //使用lio_listio发起一系列请求, LIO_WAIT
    ret = lio_listio(LIO_WAIT,listio, MAX_LIST,NULL);

    //当异步读写都完成时获取他们的返回值

    ret = aio_return(&rd);
    free((char*)rd.aio_buf);
    printf("\n读返回值:%d",ret);

    ret = aio_return(&wr);
    free((char*)wr.aio_buf);
    printf("\n写返回值:%d",ret);
    return 0;
}

// 采用异步回调方法时调用的回调函数
void aio_completion_handler(sigval_t sigval) {
    struct aiocb *prd;
    int ret;
    prd = (struct aiocb *)sigval.sival_ptr;
    printf("use call_back function  threadid=%lld\n", gettid());
    //获取返回值
    ret = aio_return(prd);
}

// 异步io采用回调的测试方法
int test_lio_listio_nowait() {
    int fd,ret;
    struct aiocb rd;

    fd = open("test.txt",O_RDONLY);
    if(fd < 0) {
        perror("test.txt");
    }

    //填充aiocb的基本内容
    bzero(&rd,sizeof(rd));

    rd.aio_fildes = fd;
    rd.aio_buf = (char *)malloc(sizeof(BUFFER_SIZE + 1));
    rd.aio_nbytes = BUFFER_SIZE;
    rd.aio_offset = 0;

    //填充aiocb中有关回调通知的结构体sigevent
    if(0) { // 此方式很少使用，
        rd.aio_sigevent.sigev_notify = SIGEV_THREAD_ID;//使用线程回调通知
        rd.aio_sigevent._sigev_un._tid =  gettid();
    } else {
        rd.aio_sigevent.sigev_notify = SIGEV_THREAD;//使用线程回调通知
    }
    rd.aio_sigevent.sigev_notify_function = aio_completion_handler;//设置回调函数
    rd.aio_sigevent.sigev_notify_attributes = NULL;//使用默认属性
    rd.aio_sigevent.sigev_value.sival_ptr = &rd;//在aiocb控制块中加入自己的引用

    //异步读取文件
    ret = aio_read(&rd);
    if(ret < 0)
    {
        perror("aio_read");
    }

    printf("异步读以开始, threadid=%lld\n", gettid());
    sleep(2);
    printf("异步读结束\n");
    return 0;
}

int main(int argc,char **argv)
{
//    return test_aio_read();
//    return test_aio_suspend();
//    return test_lio_listio_wait();
    return test_lio_listio_nowait();
}

